﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using TJDevHouse.Starcraft.Game;

namespace TJDevHouse.Starcraft.AIModuleConnector.ImportExport
{
	class CompactMapDataImportExporter
	{
		//bitmask definitions
		const int BM_BUILDABLE = 4;
		const int BM_WALKABLE = 2;
		const int BM_HIGHGROUND = 1;


		public void MakeExportMapData(Map m, System.IO.StreamWriter stream)
		{
			string head = "";
			head += m.MapHash.ToString();
			head += ":";
			head += m.MapName;
			head += ":";
			head += m.MapBuildWidth;
			head += ":";
			head += m.MapBuildHeight;

			stream.WriteLine(head);

			string startingLocations = "";
			foreach (StartingLocation sl in m.StartingLocations)
			{
				if (startingLocations != "")
				{
					startingLocations += " ";
				}
				startingLocations += sl.X + ":" + sl.Y;
			}
			stream.WriteLine(startingLocations);

			string mineralPositions = "";
			foreach (StaticMapResource min in m.StaticMinerals)
			{
				if (mineralPositions != "")
				{
					mineralPositions += " ";
				}
				mineralPositions += min.BuildTile.X + ":" + min.BuildTile.Y;
			}
			stream.WriteLine(mineralPositions);

			string gasPositions = "";
			foreach (StaticMapResource gas in m.StaticVespeneGeysers)
			{
				if (gasPositions != "")
				{
					gasPositions += " ";
				}
				gasPositions += gas.BuildTile.X + ":" + gas.BuildTile.Y;
			}
			stream.WriteLine(gasPositions);


			int byteCount = 0;
			string row = "";
			foreach (WalkTile wt in m.WalkTiles)
			{
				
				byteCount++;

				row += CalcTileBitMask(wt).ToString();


				if (wt.X == m.MapWalkableWidth - 1)
				{
					stream.WriteLine(row);
					row = "";
				}

				
			}

			stream.Flush();
			Console.WriteLine(byteCount.ToString() + " bytes of map terrain data written");
		}

		/// <summary>
		/// Calculates the bitmask for the given walk tile.
		/// 
		/// 1 = buildable
		/// 2 = walkable
		/// 4 = high ground
		/// </summary>
		/// <param name="wt"></param>
		/// <returns></returns>
		int CalcTileBitMask(WalkTile wt)
		{
			int bm = 0;
			
			if (wt.IsBuildable)
			{
				bm += BM_BUILDABLE;
			}
			
			if (wt.IsWalkable)
			{
				bm += BM_WALKABLE;
			}

			if (wt.IsHighGround)
			{
				bm += BM_HIGHGROUND;
			}
			

			return bm;
		}

		public Map MakeMapFromMapData(System.IO.StreamReader reader)
		{
			int hash, width, height;
			string name;

			List<BuildTileMapCoordinate> startingPositions, mineralPositions, geyserPositions;


			string[] headBits = reader.ReadLine().Split(':');
			hash = int.Parse(headBits[0]);
			name = headBits[1];
			width = int.Parse(headBits[2]);
			height = int.Parse(headBits[3]);

			startingPositions = ParseRowOfCoords(reader.ReadLine());
			mineralPositions = ParseRowOfCoords(reader.ReadLine());
			geyserPositions = ParseRowOfCoords(reader.ReadLine());
		
			Map map = new Map(hash, name, width, height);
			
			foreach(BuildTileMapCoordinate sl in startingPositions)
			{
				map.AddStartingLocation(sl);
			}

			foreach (BuildTileMapCoordinate min in mineralPositions)
			{
				map.AddStaticMineral(min);
			}

			foreach (BuildTileMapCoordinate gas in geyserPositions)
			{
				map.AddStaticGeyser(gas);
			}

			//used for-loops instead of do-while on stream EOF to ensure exception thrown
			//if any map data missing
			for (int y = 0; y < height * 4; y++)
			{
				char[] tiles = reader.ReadLine().ToCharArray();

				for (int x = 0; x < width * 4; x++)
				{
					TempMapTile tempTile = ParseTileDataChar(tiles[x]);

					WalkTile wt = map.GetWalkTile(x, y);
					wt.IsWalkable = tempTile.Walkable;
					wt.IsHighGround = tempTile.HighGround;

					if (x % 4 == 0 && y % 4 == 0)
					{
						wt.BuildTile.IsBuildable = tempTile.Buildable;
					}
				}
			}			
			
			return map;
		}

		public TempMapTile ParseTileDataChar(int tileData)
		{
			TempMapTile tempTile = new TempMapTile();

			tempTile.HighGround = GetHeightFromBitMask(tileData);
			tempTile.Walkable = GetWalkableFromBitMask(tileData);
			tempTile.Buildable = GetBuildableFromBitMask(tileData);

			return tempTile;
		}

		public bool GetHeightFromBitMask(int bitMask)
		{
			return (bitMask & BM_HIGHGROUND) == BM_HIGHGROUND;
		}

		public bool GetWalkableFromBitMask(int bitMask)
		{
			return BM_WALKABLE == (bitMask & BM_WALKABLE);
		}


		public bool GetBuildableFromBitMask(int bitMask)
		{
			return BM_BUILDABLE == (bitMask & BM_BUILDABLE);
		}

		/// <summary>
		/// Takes a row of coordinates in compact style and converts to a list of 
		/// build tile coords
		/// </summary>
		/// <param name="coords"></param>
		/// <returns></returns>
		public List<BuildTileMapCoordinate> ParseRowOfCoords(string coords)
		{
			List<BuildTileMapCoordinate> outCoords = new List<BuildTileMapCoordinate>();
			string[] bits = coords.Split(' ');
			foreach (string aCoord in bits)
			{
				string[] cBits = aCoord.Split(':');
				BuildTileMapCoordinate outCoord = new BuildTileMapCoordinate(int.Parse(cBits[0]), int.Parse(cBits[1]));
				outCoords.Add(outCoord);
			}

			return outCoords;
		}
	}
}
